/**
 * 
 */
package com.nextive.interview.service.impl;

import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;

import android.content.Context;
import android.database.Cursor;

import com.nextive.interview.dao.WeightInfoDao;
import com.nextive.interview.dao.sqlite.SqLiteWeightInfoDao;
import com.nextive.interview.dto.UserPreferences;
import com.nextive.interview.dto.WeightDateSingleLog;
import com.nextive.interview.service.UserInfoService;
import com.nextive.interview.service.UserInfoService.Metric;
import com.nextive.interview.service.WeightInfoService;

import static com.nextive.interview.util.DateUtil.generateCalendar;

/**
 * Implementation class of <code>WeightInfoService</code>. It is in charge of implementing 
 * all services related to weight histories. This is done via <code>WeightInfoDao</code>. 
 * @author Cesar Roman
 *
 */
public class WeightInfoServiceImpl implements WeightInfoService {

	/**
	 * Weight info dao.
	 */
	private WeightInfoDao weightInfoDao;
	
	/**
	 * User information service. Is is used to obtain user prefered metric.
	 */
	private UserInfoService userInfoService;
	
	/**
	 * Conversion to be used when converting to/from pounds.
	 */
	private static final double POUNDS_CONVERSION = 0.4536d;
	
	/**
	 * Default constructor.
	 * @param context
	 */
	public WeightInfoServiceImpl(Context context) {
		
		this.weightInfoDao = new SqLiteWeightInfoDao(context);
		this.userInfoService = new UserInfoServiceImpl(context);
		
	}
	
	/**
	 * It fetches weight information for given id.
	 * @param id
	 * @return dto containing all info
	 */
	@Override
	public WeightDateSingleLog fetchWeightInfo(long id) {
		
		Cursor cursor = null;
		try {

			cursor = weightInfoDao.fetchWeightHistoryInfo(id);
			if (cursor.moveToFirst()) {

				return new WeightDateSingleLog(
						cursor.getLong(cursor.getColumnIndex(WeightInfoDao.ID_KEY)),
						makeWeightConversion(
								cursor.getDouble(cursor.getColumnIndex(WeightInfoDao.WEIGHT_KEY)), false),
						generateCalendar(cursor.getLong(cursor.getColumnIndex(WeightInfoDao.DATE_KEY))));

			}

			return null;

		} finally {
			
			if (cursor != null)
				cursor.close();
			
		}
		
	}
	
	/**
	 * It fetches last registered weight.
	 * @return
	 */
	@Override
	public Double fetchLastRegisteredWeight() {
		
		List<WeightDateSingleLog> resultList = doFetchWeightInfo(1);
		if (resultList == null || resultList.isEmpty())
			return null;
		
		WeightDateSingleLog weightDateLog = resultList.get(0);
		return weightDateLog.getWeight();
		
	}
	
	/**
	 * It fetches all weight info entries.
	 * @return list of all weight entries
	 */
	@Override
	public List<WeightDateSingleLog> fetchAllWeightInfo() {
		
		return doFetchWeightInfo(WeightInfoDao.ALL_RESULTS);
		
	}
	
	/**
	 * It fetches all weight info entries since a given date.
	 * @param calendar
	 * @return a list of weight entries
	 */
	@Override
	public List<WeightDateSingleLog> fetchWeightInfoSince(Calendar calendar) {
		
		if (calendar != null) {
		
			Cursor cursor = null;
			try {

				cursor = weightInfoDao.fetchWeightHistoryInfoSince(calendar.getTimeInMillis());
				List<WeightDateSingleLog> result = new LinkedList<WeightDateSingleLog>();
				while (cursor.moveToNext()) {

					result.add(new WeightDateSingleLog(
							cursor.getLong(cursor.getColumnIndex(WeightInfoDao.ID_KEY)),
							makeWeightConversion(
									cursor.getDouble(cursor.getColumnIndex(WeightInfoDao.WEIGHT_KEY)), false),
							generateCalendar(cursor.getLong(cursor.getColumnIndex(WeightInfoDao.DATE_KEY)))));

				}

				return result;

			} finally {
				
				if (cursor != null)
					cursor.close();
				
			}
			
		}
		
		return null;
		
	}

	/**
	 * It deletes a weight entry given its id.
	 * @param id
	 * @return true if deleted successfully
	 */
	@Override
	public boolean deleteWeightInfo(long id) {
		
		return weightInfoDao.deleteWeightInfo(id);
		
	}

	/**
	 * It creates a new weight entry.
	 * @param weightDateLog
	 * @return newly row id
	 */
	@Override
	public long createNewWeightEntry(WeightDateSingleLog weightDateLog) {
		
		return weightInfoDao.createNewWeightInfo(
				makeWeightConversion(weightDateLog.getWeight(), true), 
				weightDateLog.getCalendar().getTimeInMillis());
		
	}

	/**
	 * It updates a single weight info entry.
	 * @param weightDateLog
	 * @return true if updated successfully
	 */
	@Override
	public boolean updateWeightInfo(WeightDateSingleLog weightDateLog) {
		
		return weightInfoDao.updateWeightInfo(weightDateLog.getId(), 
				makeWeightConversion(weightDateLog.getWeight(), true), 
				weightDateLog.getCalendar().getTimeInMillis());
		
	}

	/**
	 * It makes the weight conversion. If original weight is in <code>KG</code> then there 
	 * is no need to do any conversion. If original weight is in <code>LB</code> then we 
	 * need to convert it to <code>KG</code>. This is done by multiplying original weight 
	 * by <code>POUNDS_CONVERSION</code>. 
	 * @param originalWeight
	 * @param isStoring
	 * @return converted weight
	 */
	private Double makeWeightConversion(Double originalWeight, boolean isStoring) {

		UserPreferences preferences = userInfoService.fetchUserInfo();
		if (!Metric.KG.equals(preferences.getMetricOption())) {
			
			if (isStoring)
				return originalWeight * POUNDS_CONVERSION;
			
			return originalWeight / POUNDS_CONVERSION;
			
		}
		
		return originalWeight;
		
	}

	/**
	 * Internal method to fetch weight history entries.
	 * @param maxResults
	 * @return
	 */
	private List<WeightDateSingleLog> doFetchWeightInfo(int maxResults) {
		
		Cursor cursor = null;
		try {

			List<WeightDateSingleLog> result = new LinkedList<WeightDateSingleLog>();
			cursor = weightInfoDao.fetchWeightHistoriesInfo(maxResults);
			while (cursor.moveToNext()) {
				
				result.add(new WeightDateSingleLog(
						cursor.getLong(cursor.getColumnIndex(WeightInfoDao.ID_KEY)),
						makeWeightConversion(cursor.getDouble(cursor.getColumnIndex(WeightInfoDao.WEIGHT_KEY)), false),
						generateCalendar(cursor.getLong(cursor.getColumnIndex(WeightInfoDao.DATE_KEY)))));
				
			}
			
			return result;

		} finally {
			
			if (cursor != null)
				cursor.close();
			
		}

	}
	
}